package com.atguigu.tingshu.common.login.aspect;

/**
 * @BelongsProject: tingshu-backend
 * @BelongsPackage: com.atguigu.tingshu.common.login.aspect
 * @Author: coilk
 * @CreateTime: 2025-03-11  19:35
 * @Description:
 * @Version: 1.0
 */

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.atguigu.tingshu.common.constant.PublicConstant;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.login.annotation.TingshuLogin;
import com.atguigu.tingshu.common.result.ResultCodeEnum;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.jwt.Jwt;
import org.springframework.security.jwt.JwtHelper;
import org.springframework.security.jwt.crypto.sign.RsaVerifier;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;
import java.util.Map;

/**
 * @description: 用于登录的切面
 * @author: coilk
 * @date: 2025/3/11 19:35
 * @param: null
 * @return: null
 **/

@Aspect
@Component
@Slf4j
public class LoginAspect {

    @Autowired
    StringRedisTemplate redisTemplate;

    @Around(value = "@annotation(com.atguigu.tingshu.common.login.annotation.TingshuLogin)")
    public Object logincheck(ProceedingJoinPoint pjp) throws Throwable {

        //获取请求中的令牌
        String jsonWebToken = getJsonWebToken();

        //获取注解属性值
        Boolean isLogin = getMethodLoginAnnotationValue(pjp);
        if (!isLogin) {
            if (StringUtils.isEmpty(jsonWebToken)) {
                return pjp.proceed();
            }
        }

        //判断获取令牌中的userId
        Long userId = checkTokenAndGetUserId(jsonWebToken);

        //将userId放入ThreadLocal中
        AuthContextHolder.setUserId(userId);

        //创建通知对象
        Object retVal;

        //执行目标方法
        try {
            retVal = pjp.proceed();
        } finally {
            AuthContextHolder.removeUserId();
        }

        //返回通知对象
        return retVal;
    }

    private Boolean getMethodLoginAnnotationValue(ProceedingJoinPoint pjp) {
        MethodSignature methodSignature = (MethodSignature)pjp.getSignature();
        Method method = methodSignature.getMethod();
        TingshuLogin annotation = method.getAnnotation(TingshuLogin.class);
        return annotation.required();
    }

    private Long checkTokenAndGetUserId(String jsonWebTokenFromWeb) {
        //判断是否携带了token
        if (StringUtils.isEmpty(jsonWebTokenFromWeb)) {
            throw new GuiguException(ResultCodeEnum.LOGIN_AUTH);
        }
        //判断token是否被篡改
        Jwt jwt = JwtHelper.decodeAndVerify(jsonWebTokenFromWeb, new RsaVerifier(PublicConstant.PUBLIC_KEY));

        //获取载荷数据
        String claims = jwt.getClaims();
        Map map = JSONObject.parseObject(claims, Map.class);
        Object userId = map.get("id");
        Object openId = map.get("openId");

        //与Redis中的token进行比较
        String accessTokenKey = RedisConstant.USER_LOGIN_KEY_PREFIX + openId;
        String accessTokenFromRedis = redisTemplate.opsForValue().get(accessTokenKey);

        if (StringUtils.isEmpty(accessTokenFromRedis) || !jsonWebTokenFromWeb.equals(accessTokenFromRedis)) {
            throw new GuiguException(401, "Token已过期");
        }

        return Long.parseLong(userId.toString());
    }

    private String getJsonWebToken() {
        // 1.获取用户的身份信息
        // 1.1获取目标请求属性对象
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        // 1.2 获取请求对象
        HttpServletRequest request = requestAttributes.getRequest();
        // 1.3 获取请求对象的请求头
        return request.getHeader("token");
    }

}
